
/*
 * Copyright (C) 2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: XMLMapField.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar.xml;

import java.util.Iterator;
import java.util.Map;

import org.dellroad.sidekar.Item;
import org.dellroad.sidekar.MapField;
import org.dellroad.sidekar.SimpleField;

/**
 * Container for a {@link MapField}'s value during the Sidekar XML import/export process.
 */
class XMLMapField<K, V> extends XMLField {

    private Map<K, V> map;
    private SimpleField<K> keyField;
    private SimpleField<V> valueField;

    private boolean haveKey;
    private XMLSimpleField nextKey;
    private int nestCount;

    // Used during unmarshalling
    private XMLMapField(XMLItem xmlItem) {
        super(xmlItem);
    }

    // Used during marshalling
    public XMLMapField(Item<?> item, MapField<K, V> field) {
        super(field.getName());
        this.map = field.get(item);
        this.keyField = field.getKeyField();
        this.valueField = field.getValueField();
    }

    // Used during marshalling
    @Override
    public boolean hasDefaultValue() {
        return this.map.isEmpty();
    }

    // Used during unmarshalling
    @Override
    protected SimpleField<?> getNestedField() {
        getMap();
        return (++this.nestCount % 2) != 0 ? this.keyField : this.valueField;
    }

    // Used during unmarshalling
    public void add(XMLSimpleField xmlField) {
        if (haveKey) {
            getMap();
            this.map.put(this.keyField.cast(this.nextKey.getObjectValue()), this.valueField.cast(xmlField.getObjectValue()));
        } else
            this.nextKey = xmlField;
        haveKey = !haveKey;
    }

    // Used during unmarshalling
    @SuppressWarnings("unchecked")
    private Map<K, V> getMap() {
        if (this.map == null) {
            MapField<K, V> field = (MapField<K, V>)getField();
            this.map = field.get(getItem());
            this.keyField = field.getKeyField();
            this.valueField = field.getValueField();
        }
        return this.map;
    }

    // Used during unmarshalling
    public Iterator<XMLSimpleField> iterator() {
        return new SimpleDatabaseIterator<XMLSimpleField, Map.Entry<K, V>>(this.map.entrySet().iterator()) {

            private Map.Entry<K, V> current;

            @Override
            public boolean hasNext() {
                return current != null || i.hasNext();
            }

            @Override
            protected XMLSimpleField getNext() {
                XMLSimpleFieldSwitch fieldSwitch;
                if (current == null) {
                    current = this.i.next();
                    fieldSwitch = new XMLSimpleFieldSwitch(current.getKey());
                    keyField.visit(fieldSwitch);
                } else {
                    fieldSwitch = new XMLSimpleFieldSwitch(current.getValue());
                    valueField.visit(fieldSwitch);
                    current = null;
                }
                return fieldSwitch.getXMLSimpleField();
            }
        };
    }

    // Used during unmarshalling
    public static <K, V> XMLMapField<K, V> newInstance(Object obj) {
        return new XMLMapField<K, V>((XMLItem)obj);
    }
}
